#include "RenzoPrecompiled.h"
#include "RenzoResourceManager.h"
#include "RenzoCamera.h"
#include "RenzoTexture.h"
#include "RenzoImage.h"
#include "RenzoMesh.h"
#include "RenzoPolygonGroup.h"

namespace Renzo
{
ResourceManager::ResourceManager(void)
{
	memset(maxIndices, 0, sizeof(ResourceIndex) * NB_RESOURCE_TYPES);
	//objectMap = new ObjectMap;
	//createFuncMap = new CreateFuncMap;
}

void ResourceManager::registerCreateFuncs() {
	///
	/// Register object creators to ResourceManager
	///
	this->registerCreateFunc(CameraType, Camera::create);
	this->registerCreateFunc(TextureType, Texture::create);
	this->registerCreateFunc(ImageType, Image::create);
	this->registerCreateFunc(PolygonGroupType, PolygonGroup::create);
	this->registerCreateFunc(MeshType, Mesh::create);

}

ResourceManager::~ResourceManager(void)
{
	//rzDel(objectMap);
	//rzDel(createFuncMap);
}

ResourceID ResourceManager::genResourceID(ObjectType type) {
	ResourceID id;
	ResourceIndex index = maxIndices[(int)type];
	/*
	if (maxIndices.find(type) != 0) {
		index = maxIndices[type] + 1;
	} else {
		index = 0;
	}
	maxIndices[type] = index;
	*/
	maxIndices[(int)type]++;
	id = (ResourceID)type; // convert to long
	id = id << 24;
	id += index;
	return id;
}

void ResourceManager::registerCreateFunc(ObjectType t, CreateFunc f) {
	
	//m[0] = 0;

	//Object* obj = Camera::create(); // RM is a friend class of Camera, so can call Camera::create, which is protected function in Camera.
	//m[1] = obj;
	//return;

	//objectMap[1] = obj;
	//createFuncMap.insert(CreateFuncMap::value_type(t, f));
	//createFuncMap[t] = f;
	//return;

	CreateFuncMap::iterator i = createFuncMap.find(t);
	if (i != createFuncMap.end()) {
		throw Exception(ERROR_CREATOR_EXISTED);
	} else {
		//createFuncMap[t] = f;
		createFuncMap.insert(CreateFuncMap::value_type(t, f));
	}
}

Object* ResourceManager::createObject(ObjectType t) {
	CreateFuncMap::iterator i = createFuncMap.find(t);
	if (i != createFuncMap.end()) {
		Object* obj = (i->second)();
		obj->setID(this->genResourceID(t));
		this->attachObject(obj);
		return obj;
	} else {
		throw Exception(ERROR_CREATOR_NOT_FOUND);
	}
}

void ResourceManager::disposeObject(Object* obj) {
	this->detachObject(obj);
	delete obj;
}

void ResourceManager::disposeAll() {
	ObjectMap::iterator i;
	for (i = objectMap.begin(); i != objectMap.end(); ++i) {
		this->disposeObject(i->second);
	}
}

Object* ResourceManager::getObject(const ResourceID& id) {
	ObjectMap::iterator i = objectMap.find(id);
	if (i != objectMap.end()) {
		return i->second;
	} else {
		throw Exception(ERROR_OBJECT_NOT_FOUND);
	}
}

Object* ResourceManager::getObjectByName(const String& name) {
	// inefficient implementation
	// TODO: enhance performance here by adding a second hash_map? This will need to maintain two hashmaps at the same time. 
	return NULL;
}

void ResourceManager::attachObject(Object* obj) {
	ObjectMap::iterator i = objectMap.find(obj->getID());
	if (i != objectMap.end()) {
		// object existed in the system
		throw Exception(ERROR_OBJECT_EXISTED);
	} else {
		objectMap[obj->getID()] = obj;
	}
}

void ResourceManager::detachObject(Object* obj) {
	this->detachObject(obj->getID());
}

void ResourceManager::detachObject(const ResourceID& id) {
	ObjectMap::iterator i = objectMap.find(id);
	if (i != objectMap.end()) {
		// object existed. Can detach
		objectMap.erase(i);
	} else {
		throw Exception(ERROR_OBJECT_NOT_FOUND);
	}
}

}